package com.simplesoftwares.rebind;

import java.io.PrintWriter;
import java.util.ArrayList;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.simplesoftwares.rebind.annatonations.EntityNameAnnotation;
/**
 * Any geneartor which geneartes a class should inherit this method.
 * @author SUMIT
 *
 */
public abstract class SuperGenerator extends Generator
{
	/** The cots. */
	String cots="\"";
	
	/**  Class object corresponding to replasable part */
	protected JClassType replasableClassType;
	
	/**
	 * Class object corresponding to entity class type
	 */
	protected JClassType entityClaasType;
	
	/** sourceWriter for current session */
	protected SourceWriter sWriter;
	
	/** Array containing imports*/
	protected ArrayList<String> imports;
	
	
	
	/** The logger. */
	protected TreeLogger logger;
	
	
	/**
	 * Helper method to create a SourceWriter object that will be used to write the new type also creates
	 * the outer body ,add imports,add superclass  for the generated class.
	 * creates the class structure
	 * @param logger The compiler's logger
	 * @param context The compiler's current context
	 * @param packageName The package name for the new type
	 * @param className The class name for the new type
	 * @param superclassName The class name the type extends
	 * @param imports Zero or more imports that the new class needs to import beyond those in the super class
	 * @return {@link SourceWriter} source writer object for current session writing
	 */
	SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext context,
			String packageName, String className, String superclassName,
			ArrayList<String>imports) 
	{

		// Create a PrintWriter object
		PrintWriter printWriter = context.tryCreate(logger, packageName,
				className);

		// If the PrintWriter object is null then the assumption is that the class has
		// already been created before, so return null to indicate that.
		if (printWriter == null)
			return null;

		// Create a factory that we will use to create the basis of our new class
		ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, className);
		//add imports
		for(int i=0;i<imports.size();i++)
				factory.addImport(imports.get(i));
	  // Set the super class name of the new class
       factory.setSuperclass(superclassName);
     

		// Create and return the SourceWrite from the factory
		return factory.createSourceWriter(context, printWriter);
	}
	/**
	 * Read the Annatonated fields
	 * @param context
	 */
	protected abstract void readAnnotatedFields();
	/**
	 * Genrate  the varialbles
	 */
	protected abstract void generateVariableDeclaration();
	/**
	 * Genrate the body of class
	 */
	protected abstract void generateclassBody();
	/**
	 * Initialize the variable
	 */
	protected abstract void generateVariableInitialization();
	/**
	 * Generates the imports
	 */
	
	protected abstract void generateImports();
	protected abstract void generategetVarRef();
	
	/**
	 * Initialize the default constructor
	 * @param className
	 */
	
	protected void generateDeConstructor(String className)
	{
		sWriter.println("public "+className+"()");
		sWriter.println("{");
		sWriter.println("super();");
		sWriter.println("}");
		
	}
	
	/**
	 * Generate the output.
	 */
	@Override
	public String generate(TreeLogger logger, GeneratorContext context,String typeName) throws UnableToCompleteException
	{
		
		//Get the type oracle
				TypeOracle types = context.getTypeOracle();
				this.logger=logger;
				
				try {
/////////////////////////Initalize the replasable class type///////////////////////////////////////
					replasableClassType = types.getType(typeName);
					
/////////////////////////Initalize Entity class type///////////////////////////////////////////////
					EntityNameAnnotation anno=replasableClassType.getAnnotation(EntityNameAnnotation.class);
					String ename=anno.EntityName();
					TypeOracle oracle=context.getTypeOracle();
					entityClaasType=oracle.findType(ename);
					} catch (NotFoundException e) 
				{
					
					e.printStackTrace();
				}
				
				
//////////////////////Get package name of replasable class type here the replased package will go///////////
				String packageName = replasableClassType.getPackage().getName();
				//Get the simpleClassName of replasable class type
				String simpleClassName = replasableClassType.getSimpleSourceName();

				//Create the simple name of class which will replase the replasable type
				String proxyName = simpleClassName + "Proxy";
				//Create the qualified name  of class which will replase the replasable type
				String qualifiedName = packageName + "." + proxyName;
                //read annatonated fields
				readAnnotatedFields();
				generateImports();
				//get the source writer and the method creates outer sctructure of class
				sWriter = getSourceWriter(logger, context,
						packageName, proxyName, simpleClassName,imports);
				//If source Writer is null we should return null
				if(sWriter==null)
					return null;
		//Genrate the variable declaration
		generateVariableDeclaration();
		generategetVarRef();
		//Genrate Constructor
		generateDeConstructor(proxyName);
	    //Initialize the variables
		generateVariableInitialization();
		//Generate the body of class
		
		generateclassBody();
		
		sWriter.commit(logger);
		
		return qualifiedName ;
	}
	/**
	 * Recursively creates an ArrayList containing all the {@link JField} in the hirerchary of class
	 * 
	 * @param classtype Class which all fields are to be calculated
	 * @param array ArrayList containing {@link JField} Arraylist of JFields
	 * @return ArrayList containing {@link JField}
	 */
	
	protected ArrayList<JField> getAllFields(JClassType classtype,ArrayList<JField>array)
	{
		//Get all the fields of class
		JField[] fieldarrat=classtype.getFields();
		//Add them in array
		if(fieldarrat!=null)
		{
			for(int i=0;i<fieldarrat.length;i++)
				array.add(fieldarrat[i]);
		}
		
		if(classtype.getSuperclass()==null)
			   return array;
		else
			getAllFields(classtype.getSuperclass(),array);
		
		
		return array;
		
	}
	
	/**
	 * Recursively creates an ArrayList containing all the {@link JMethod} in the hirerchary of class
	 * 
	 * @param classtype Class which all fields are to be calculated
	 * @param array ArrayList containing {@link JMethod} Arraylist of JFields
	 * @return ArrayList containing {@link JMethod}
	 */
	protected ArrayList<JMethod> getAllMethod(JClassType classtype,ArrayList<JMethod>array)
	{
		//Get all the fields of class
		JMethod[] fieldarrat=classtype.getMethods();
		//Add them in array
		if(fieldarrat!=null)
		{
			for(int i=0;i<fieldarrat.length;i++)
			{
				//Override m=fieldarrat[i].getAnnotation(Override.class);
				//if(m==null)
					array.add(fieldarrat[i]);
			}
		}
		
		if(classtype.getSuperclass()==null)
			   return array;
		else
			getAllMethod(classtype.getSuperclass(),array);
		
		
		return array;
		
	}
	
	

}
